package main

import (
	"bytes"
	"fmt"
	"math/rand"
	"strings"
	"sync"
	"time"
)

type Peer struct {
	Id       string `json:"id"`
	Ip       string `json:"ip"`
	Port     int    `json:"port"`
	Network  string `json:"network"`
	Version  int    `json:"version"`
	Protocol string `json:"protocol"`
}

type PeerTable struct {
	lock  *sync.Mutex
	items map[string]*Peer
	timer map[string]*time.Timer
}

func NewPeerTable() *PeerTable {
	instance := &PeerTable{}

	instance.lock = &sync.Mutex{}
	instance.items = make(map[string]*Peer)
	instance.timer = make(map[string]*time.Timer)
	return instance
}

func (self *PeerTable) Len() int {
	self.lock.Lock()
	defer self.lock.Unlock()

	return len(self.items)
}

func (self *PeerTable) ToSlice() []*Peer {
	self.lock.Lock()
	defer self.lock.Unlock()

	var result []*Peer
	for _, o := range self.items {
		result = append(result, o)
	}

	return result
}
func (self *PeerTable) GetRandom(network string) *Peer {
	peerList := self.ToSlice()
	self.lock.Lock()
	defer self.lock.Unlock()
	if network != "" {
		networks := strings.Split(network, ",")
		for i := len(peerList) - 1; i >= 0; i-- {
			if Contains(networks, peerList[i].Network) == false {
				peerList = append(peerList[:i], peerList[i+1:]...)
			}
		}
	}
	if len(peerList) == 0 {
		return nil
	}
	rand.Seed(time.Now().UnixNano())
	return peerList[int(float64(len(peerList))*rand.Float64())<<0]
}
func (self *PeerTable) IsEmpty() bool {
	return self.Len() == 0
}
func (self *PeerTable) Set(object *Peer) {
	self.lock.Lock()
	defer self.lock.Unlock()
	self.items[object.Id] = object
	if self.timer[object.Id] != nil {
		self.timer[object.Id].Stop()
	}
	self.timer[object.Id] = time.AfterFunc(35*time.Second, func() {
		self.Remove(object.Id)
	})
}
func (self *PeerTable) Add(objects ...*Peer) {
	self.lock.Lock()
	defer self.lock.Unlock()

	for _, o := range objects {
		self.items[o.Id] = o
	}
}

func (self *PeerTable) Get(k string) *Peer {
	self.lock.Lock()
	defer self.lock.Unlock()

	return self.items[k]
}

func (self *PeerTable) Contains(k string) bool {
	return self.Get(k) != nil
}

func (self *PeerTable) Remove(k string) bool {
	self.lock.Lock()
	defer self.lock.Unlock()

	found := self.items[k]
	if found != nil {
		delete(self.items, k)
		return true
	} else {
		return false
	}
}

func (self *PeerTable) Clear() {
	self.lock.Lock()
	defer self.lock.Unlock()
	self.items = make(map[string]*Peer)
}

func (self *PeerTable) String() string {
	self.lock.Lock()
	defer self.lock.Unlock()

	var buffer bytes.Buffer

	i := 0
	for k, _ := range self.items {
		stringify := fmt.Sprintf("%s", k)
		buffer.WriteString(stringify)
		if i != len(self.items)-1 {
			buffer.WriteString(", ")
		}
		i++
	}
	return fmt.Sprintf("{ %s }", buffer.String())
}
